package com.example.plugin.hook.handler;

import java.lang.reflect.Method;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.text.TextUtils;

import com.example.plugin.Env;
import com.example.plugin.PluginManager;
import com.example.plugin.hook.HookHandler;
import com.example.plugin.hook.HookedMethodHandler;
import com.example.plugin.reflect.FieldUtils;

public class ActivityManagerHookHandler extends HookHandler {

	public ActivityManagerHookHandler(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}

	@Override
	protected void init() {
		// TODO Auto-generated method stub
		sHookedMethodHandlers.put("startActivity", new startActivity(mContext));
	}

	private static class startActivity extends HookedMethodHandler {

		public startActivity(Context hostContext) {
			super(hostContext);
		}

		protected boolean doReplaceIntentForStartActivityAPIHigh(Object[] args)
				throws RemoteException {
			int intentOfArgIndex = findFirstIntentIndexInArgs(args);

			if (args != null && args.length > 1 && intentOfArgIndex >= 0) {
				Intent intent = (Intent) args[intentOfArgIndex];
				ActivityInfo activityInfo = PluginManager
						.resolveActivityInfo(intent);

				if (activityInfo != null
						&& PluginManager.isPlugin(activityInfo.packageName)) {
					ComponentName component = new ComponentName(
							mContext.getPackageName(),
							"com.example.plugin.activity.ProxyActivity");

					Intent newIntent = new Intent();
					ClassLoader pluginClassLoader = PluginManager
							.getLoader(component.getPackageName());
					setIntentClassLoader(newIntent, pluginClassLoader);
					newIntent.setComponent(component);
					newIntent.putExtra(Env.EXTRA_TARGET_INTENT, intent);
					newIntent.setFlags(intent.getFlags());

					String callingPackage = (String) args[1];
					if (TextUtils.equals(mContext.getPackageName(),
							callingPackage)) {
						newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
					}

					args[intentOfArgIndex] = newIntent;
					args[1] = mContext.getPackageName();
				}
			}

			return true;
		}

		@Override
		protected boolean beforeInvoke(Object receiver, Method method,
				Object[] args) throws Throwable {
			// TODO Auto-generated method stub
			if (!doReplaceIntentForStartActivityAPIHigh(args)) {
				setFakedResult(Activity.RESULT_CANCELED);
				return true;
			}
			return super.beforeInvoke(receiver, method, args);
		}
	}

	private static void setIntentClassLoader(Intent intent,
			ClassLoader classLoader) {
		try {
			Bundle mExtras = (Bundle) FieldUtils.readField(intent, "mExtras");
			if (mExtras != null) {
				mExtras.setClassLoader(classLoader);
			} else {
				Bundle value = new Bundle();
				value.setClassLoader(classLoader);
				FieldUtils.writeField(intent, "mExtras", value);
			}
		} catch (Exception e) {
		} finally {
			intent.setExtrasClassLoader(classLoader);
		}
	}

	private static int findFirstIntentIndexInArgs(Object[] args) {
		if (args != null && args.length > 0) {
			int i = 0;
			for (Object arg : args) {
				if (arg != null && arg instanceof Intent) {
					return i;
				}
				i++;
			}
		}
		return -1;
	}
}
